#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

#include <unistd.h>
#include <pthread.h>

#include "db.h"
#include "timer.h"
#include "mail.h"
#include "trace.h"

#define DB_FILE_PATH "log2mail.db"
#define LOG_FILE_PATH "/var/log/messages"

volatile int need_stop = 0;
volatile int is_time_to_send = 0;

void * parser_task(void * arg)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	FILE * fd = fopen(LOG_FILE_PATH, "r");
	if (fd == NULL)
	{
		TRACE_ERR("<%s> fopen failed\n", __FUNCTION__);
		exit(EXIT_FAILURE);
	}

	sqlite3 * db = db_connection_open(DB_FILE_PATH);
	if (NULL == db)
	{
		TRACE_ERR("<%s> db_connection_open failed\n", __FUNCTION__);
		db_connection_close(db);
		exit(EXIT_FAILURE);
	}

	if (0 != db_table_create(db))
	{
		TRACE_ERR("<%s> db_table_create failed\n", __FUNCTION__);
		db_connection_close(db);
		exit(EXIT_FAILURE);
	}

	char * str = NULL;
	size_t len = 0;
	size_t line = 0;
	while (0 == need_stop)
	{
		ssize_t rlen = 0;
		if (-1 == (rlen = getline(&str, &len, fd)))
		{
			usleep(1 * 1000 * 1000);
			continue;
		}
		else
		{
			if (0 != rlen)
			{
				if (0 != db_entry_insert(db, str, ++line))
				{
					TRACE_ERR("<%s> db_entry_insert failed\n", __FUNCTION__);
				}
			}
		}
	}
	fclose(fd);
	free(str);
	db_connection_close(db);
	return NULL;
}

void * sender_task(void * arg)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	sqlite3 * db = db_connection_open(DB_FILE_PATH);
	if (NULL == db)
	{
		TRACE_ERR("<%s> db_connection_open failed\n", __FUNCTION__);
		db_connection_close(db);
		exit(EXIT_FAILURE);
	}

	if (0 != db_table_create(db))
	{
		TRACE_ERR("<%s> db_table_create failed\n", __FUNCTION__);
		db_connection_close(db);
		exit(EXIT_FAILURE);
	}

	struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; clockgettime(&timeout); timeout.tv_sec += NEW_ENTRIES_SECONDS;

	while (0 == need_stop)
	{
		struct timeval now = { .tv_sec = 0, .tv_usec = 0 }; clockgettime(&now);
		if (0 != is_timer_expired(&timeout, &now))
		{
			timeout.tv_sec = now.tv_sec + NEW_ENTRIES_SECONDS; timeout.tv_usec = now.tv_usec;
			is_time_to_send = 1;
		}
		size_t new_entries = db_new_entries_count(db);
		if (NEW_ENTRIES_AMOUNT <= new_entries)
		{
			is_time_to_send = 1;
		}

		if (0 != is_time_to_send)
		{
			is_time_to_send = 0;
			if (0 < new_entries) db_new_entries_send(db);
		}

		usleep(1 * 1000 * 1000);
	}

	db_connection_close(db);
	return NULL;
}

int parse_options(int argc, char ** argv)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	void show_help()
	{
		printf("log2mail parse /var/log/messages file and send new messages to mail recipients via SMTP.\n");
		printf("Mandatory options:\n");
		printf("-h\n");
		printf("\tShow this message.\n");
		printf("-s\n");
		printf("\tSpecify SMTP server and port ('smtp://example.org:25' for example).\n");
		printf("-u\n");
		printf("\tSpecify SMTP username.\n");
		printf("-p\n");
		printf("\tSpecify SMTP password.\n");
	}

	if (NULL == argv)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return 1;
	}

	opterr = 0;
	int c;
	while ((c = getopt(argc, argv, ":s:u:p:h")) != -1)
	{
		switch (c)
		{
			case 's':
			{
				mail_server_set(optarg);
				break;
			}
			case 'u':
			{
				mail_username_set(optarg);
				break;
			}
			case 'p':
			{
				mail_password_set(optarg);
				break;
			}
			//case '?':
			//case 'h':
			default:
			{
				show_help();
				return 1;
			}
		}
	}

	if (0 == mail_is_configured())
	{
		show_help();
		return 1;
	}

	return 0;
}

int main (int argc, char ** argv)
{
	if (0 != parse_options(argc, argv))
	{
		return EXIT_FAILURE;
	}

	pthread_t parser_thread;
	int rc = 0;
	if (0 != (rc = pthread_create(&parser_thread, NULL, parser_task, NULL)))
	{
		TRACE_ERR("<%s> pthread_create failed\n", __FUNCTION__);
		return EXIT_FAILURE;
	}
	pthread_t sender_thread;
	if (0 != (rc = pthread_create(&sender_thread, NULL, sender_task, NULL)))
	{
		TRACE_ERR("<%s> pthread_create failed\n", __FUNCTION__);
		return EXIT_FAILURE;
	}

	char * str = NULL;
	size_t len = 0;
	while (0 == need_stop)
	{
		if (-1 == getline(&str, &len, stdin))
		{
			usleep(1 * 1000 * 1000);
			continue;
		}
		else
		{
			if (0 == strcmp("s\n", str))
			{
				TRACE_DBG("<%s> send\n", __FUNCTION__);
				is_time_to_send = 1;
			}
			else if (0 == strcmp("q\n", str))
			{
				TRACE_DBG("<%s> quit\n", __FUNCTION__);
				need_stop = 1;
			}
			else
			{
				printf("q<Enter> - quit.\ns<Enter> - send collected log entries immediately.\n");
			}
		}
	}
	free(str);

	if (0 != (rc = pthread_join(parser_thread, NULL)))
	{
		TRACE_ERR("<%s> pthread_join failed\n", __FUNCTION__);
		return EXIT_FAILURE;
	}
	if (0 != (rc = pthread_join(sender_thread, NULL)))
	{
		TRACE_ERR("<%s> pthread_join failed\n", __FUNCTION__);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

